package cn.seaboot.commons.http;

import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;

import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;

/**
 * 简单的配置管理类
 *
 * @author Mr.css
 * @version 2019-9-25 9:57
 */
public class HttpClient implements Closeable {

    /**
     * 这个参数是必须的
     */
    private CloseableHttpClient httpClient;

    /**
     * 使用配置的数据，构造一个Client
     *
     * @param httpProperties 配置信息
     */
    public void withProperties(HttpProperties httpProperties) {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();

        // 连接配置
        ConnectionConfig connectionConfig = ConnectionConfig.custom()
                .setConnectTimeout(httpProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
                .setSocketTimeout(httpProperties.getSocketTimeout(), TimeUnit.MILLISECONDS)
                .build();

        // Use TLS v1.3 only
        // connManager.setDefaultTlsConfig(TlsConfig.custom()
        //         .setHandshakeTimeout(Timeout.ofSeconds(30))
        //         .setSupportedProtocols(TLS.V_1_0, TLS.V_1_1, TLS.V_1_2, TLS.V_1_3)
        //         .build());

        // 连接管理
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(httpProperties.getMaxTotal());
        connectionManager.setDefaultMaxPerRoute(httpProperties.getMaxPerRoute());
        connectionManager.setDefaultConnectionConfig(connectionConfig);

        // 请求管理
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(httpProperties.getConnectionRequestTimeout(), TimeUnit.MILLISECONDS)
                .setCookieSpec("ignoreCookies")
                .setRedirectsEnabled(false).build();

        // 构建 client 对象
        this.httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }

    /**
     * 使用默认的 HttpClient 配置，通常用于调试
     */
    public void withDefaultSetting() {
        httpClient = HttpClients.custom().build();
    }

    /**
     * 构建一个 HttpRequest
     *
     * @param url url
     * @return HttpRequest
     */
    public HttpRequest request(String method, URI url) {
        return new HttpRequest(this.httpClient, method, url);
    }

    /**
     * 构建一个 HttpRequest
     *
     * @param url url
     * @return HttpRequest
     */
    public HttpRequest get(URI url) {
        return new HttpRequest(httpClient, RequestMethod.GET, url);
    }

    /**
     * 构建一个 HttpRequest
     *
     * @param url url
     * @return HttpRequest
     */
    public HttpRequest post(URI url) {
        return new HttpRequest(httpClient, RequestMethod.POST, url);
    }

    /**
     * 构建一个 HttpRequest
     *
     * @param url url
     * @return HttpRequest
     */
    public HttpRequest delete(URI url) {
        return new HttpRequest(httpClient, RequestMethod.DELETE, url);
    }

    /**
     * 构建一个 HttpRequest
     *
     * @param url url
     * @return HttpRequest
     */
    public HttpRequest put(URI url) {
        return new HttpRequest(httpClient, RequestMethod.PUT, url);
    }

    // getter/setter -------------------------------------------------------------------

    public CloseableHttpClient getOriginalHttpClient() {
        return httpClient;
    }

    public void setOriginalHttpClient(CloseableHttpClient httpClient) {
        this.httpClient = httpClient;
    }

    @Override
    public void close() {
        try {
            this.httpClient.close();
            this.httpClient = null;
        } catch (IOException e) {
            throw new HttpException(e);
        }
    }
}
